/*
 * TranGateDescription.java
 * 
 * last update: 16.01.2010 by Stefan Saru
 * 
 * author:	Alec(panovici@elcom.pub.ro)
 * 
 * Obs:
 */

package engine;

import java.util.*;
import java.io.*;
import middle.*;

/**
 * Transistors:
 * [r]tran, [r]tranif(0|1)
 * There is no associated TranGate class, since
 * a tran is a pair of MContAssignMonitors (to which I have
 * added reduceing strength capabiliies).
 ***
 * Since the standard does not specify the exact behaviour of tran gates
 * (no truth tables & stuff), I suppose the most probable one
 * should be as foloows: if the control wire (we're spaking about
 * tranif?) has any value other than 1, the tranif? is off.
 */

@SuppressWarnings("serial")
class TranGateDescription extends StdGateDescription
{

	TranGateDescription (byte type) {
		super(type, type == tran || type == rtran ? 0 : 2);
	}

	///////////////////////////////
	// ModuleFactory implementation
	///////////////////////////////

	public Object createNewModule(NameSpace parent,
			ModuleInstanceDescription miDesc,
			byte strength,
			ScopeNode scope)
	throws ParseException
	{
		xConsole.debug("creating new TranGate");

		try {
			if (miDesc.portsVector.size() < 2)
				throw new ParseException("wrong number of ports");
			ExpressionDescription port1, port2, control;
			Enumeration e = miDesc.portsVector.elements();
			port1 = (ExpressionDescription) e.nextElement();
			port2 = (ExpressionDescription) e.nextElement();
			Expression p1, p2, ctrl;

			if (port1 == null ||  port2 == null) {
				xConsole.warn(parent.desc.toString(miDesc.lineNo) +
						"warning: tran not created: one data port is HiZ");
				return null;
			}

			preparePortExpression(port1);
			preparePortExpression(port2);

			if (type == tran || type == rtran) {
				try {
					return new TranGate(parent,
							(WireSelection)port1.instantiate(parent),
							(WireSelection)port2.instantiate(parent),
							miDesc, type);
				} catch (ClassCastException cex) {
					throw new ParseException("wrong port type: not a lValue");
				}
			} else {
				if (!e.hasMoreElements())
					throw new ParseException("missing control port");
				control = (ExpressionDescription) e.nextElement();
				if (control == null) {
					xConsole.warn(parent.desc.toString(miDesc.lineNo) +
							"warning: tranif? not created: control port is HiZ");
					return null;
				}
				try {
					return new TranGate(parent, delay,
							(WireSelection)port1.instantiate(parent),
							(WireSelection)port2.instantiate(parent),
							control.instantiate(parent),
							miDesc, type);
				} catch (ClassCastException cex) {
					throw new ParseException ("wrong port type: not a lValue");
				}
			}
		} catch (ParseException pex) {
			throw new ParseException(parent.desc.toString(miDesc.lineNo) +
					": error: "  + pex);
		}
	}

	/**
	 * 
	 */
	public Object createNewModuleArray(NameSpace parent,
			ModuleInstanceDescription miDesc,
			byte strength,
			ScopeNode scope, int ars, int are,
			int n, int increment)
	throws ParseException
	{

		xConsole.debug("creating new TranGate[]");
		try {
			if (miDesc.portsVector.size() < 2)
				throw new ParseException("wrong number of ports");
			ExpressionDescription port1, port2, control;
			Enumeration e = miDesc.portsVector.elements();
			port1 = (ExpressionDescription) e.nextElement();
			port2 = (ExpressionDescription) e.nextElement();
			WireSelection p1, p2;

			boolean isTran = type == tran || type == rtran;

			WireSelection portsA[] = new WireSelection[n];
			WireSelection portsB[] = new WireSelection[n];

			Monitor m;
			TranGate trans[] = new TranGate[n];

			if (port1 == null ||  port2 == null) {
				xConsole.warn(parent.desc.toString(miDesc.lineNo) +
						"warning: tran not created: one data port is HiZ");
				return null;
			}

			preparePortExpression(port1);
			preparePortExpression(port2);

			try {
				p1 = (WireSelection) port1.instantiate(parent);
			} catch (ClassCastException cex) {
				throw new ParseException("bad port expression #1: not a net");
			}

			try {
				p2 = (WireSelection) port2.instantiate(parent);
			} catch (ClassCastException cex) {
				throw new ParseException("bad port expression #2: not a net");
			}

			makeInternalPorts(parent, port1, p1, portsA, n);
			makeInternalPorts(parent, port2, p2, portsB, n);

			if (isTran) {
				try {
					for (int i = 0; i < n; i++)
						trans[i] = new TranGate(parent,
								portsA[i],
								portsB[i],
								miDesc, type);
					return trans;
				} catch (ClassCastException cex) {
					throw new ParseException("wrong port type: not a lValue");
				}
			} else {
				Expression ctrls[] = new Expression[n];
				Expression ctrl = null;

				if (!e.hasMoreElements())
					throw new ParseException("missing control port");
				control = (ExpressionDescription) e.nextElement();
				if (control == null) {
					xConsole.warn(parent.desc.toString(miDesc.lineNo) +
							"warning: tranif? not created: control port is HiZ");
					return null;
				} else
					ctrl = control.instantiate(parent);

				if (ctrl.length != 1) {
					if (ctrl.length != n)
						throw new ParseException("bad port experssion length for port #3");
					Wire buffer = new Wire (null, null, Assignable.typeWire,
							n - 1, 0,
							WireDescription.defaultExpandType,
							Delay3.nullDelay());
					m = MContAssignMonitor.
					createNewMContAssignMonitor(new ContBitSelect(parent, buffer,
							null, n-1, 0), ctrl);
					for (int i = 0; i < n ; i--)
						ctrls[i] = new BitSelect(parent, buffer, null, i, i);
				} else {
					ctrls[0] = ctrl;
					for (int i = 1; i < n; i++)
						ctrls[i] = control.instantiate(parent);
				}
				for(int i = 0; i < n; i++)
					trans[i] =  new TranGate(parent, delay,
							portsA[i],
							portsB[i],
							ctrls[i],
							miDesc, type);
				return trans;
			}
		} catch (ParseException pex) {
			throw new ParseException(parent.desc.toString(miDesc.lineNo) +
					": error: "  + pex);
		}
	}


	void makeInternalPorts(NameSpace parent, ExpressionDescription portDesc,
			WireSelection p, WireSelection intern[], int n)
	throws ParseException
	{
		Monitor m;
		if (p.length != 1) {
			if (p.length != n)
				throw new ParseException("bad port experssion length for port #1");
			p.breakSelection(intern, 0);
			p.release();
		} else {
			intern[0] = p;
			for (int i = 1; i < n ; i--)
				intern[i] = (WireSelection) portDesc.instantiate(parent);
		}
	}


	void preparePortExpression(ExpressionDescription port)
	throws ParseException
	{
		try {
			((AssignableSelection)port).toContSelection();
		} catch (ClassCastException cex) {
			throw new ParseException("bad port expression \"" +
					port + "\" not a net");
		}
	}

}






